<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>发布者订阅模式</title>
</head>

<body>
    <script>
        /*
        发布订阅模式
            + 有一个对象，有人一直看着他
            + 当这个对象发生变化的时候，第三方通知这个看着的人，触发技能
            + 例子：买书
            1.普通程序员买书
                =>去书店，问，没有
                =>回家
                =>过一会再问，没有，回家
                =>过一会再问，没有，回家(直到有)
            2.发布订阅的程序员
                =>去书店，问，没有，留下一个联系方式给店员
                =>一旦有了书，就会接到电话
                =>触发技能去买书
                =>
            +只需要一个构造函数，
             =>创建一个第三方店员的身份，其实发布订阅模式在开发中用的特别多，就是事件监听addEventListener
                就是浏览器在看着div，一旦用户在页面触发了click行为，就马上执行相应函数
             =>模拟一个 addEventListener()
        
        分析构造函数
            +属性：消息队列
            {
                click:p[fn1,fn2],
                abs:[fn1,fn2,fn3,]
            }
            +方法：能向消息队列里面添加内容
            +方法：能删除消息队列里面内容
            +方法：能触发消息队列里面内容

        */
        //    创建一个第三方观察着构造函数
        class Observer {
            constructor() {
                this.message = {}
            }
            // 1.向消息队列里面添加内容
            on(type, fn) {
                // type我拜托你看着的行为
                // fn 我要你都在行为发生的时候做的事情
                // 就应该把这些记录表在消息队列[]里面
                // 1.判断message里面有没有这个行为已经被注册过了
                // 如果没有，我应该赋值一个这个行为,赋值为数组
                // 如果有，这直接向数组里面添加就可以了
                if (!this.message[type]) {
                    // 表示消息队列里面还没有注册过
                    this.message[type] = []
                    console.log('我执行了!')
                }
                // 直接进行push
                this.message[type].push(fn)
            }

            // 2.删除消息队列里面的内容
            off(type, fn) {
                // 传到这里的有两种情况
                // 1. 判断如果fn不存在就只有一个type参数的情况
                if (!fn) {
                    // 直接把这个事情取消掉
                    // 从message把成员删除
                    delete this.message[type]
                    return
                }
                // 代码能来到这里表示fn存在
                // 判断是不是真的订阅过
                if (!this.message[type]) return
                // 如果确实订阅过，就可以使用filter删除 
                this.message[type] = this.message[type].filter(item => item !== fn)
            }
            // 3.触发消息队列里面的内容
            trigger(type, fn) {
                // 判断是不是有订阅过
                if (!this.message[type]) return
                this.message[type].forEach(item => {
                    // item就是每一个函数 
                    item()
                });
            }
        }
        // 适用构造函数创建一个实例
        const person1 = new Observer()
        // 当你想拜托这个person1帮你观察一些内容的时候，
        // 你告诉person1一个行为，当行为出现的时候，person1该干什么，相当于订阅一个行为
        person1.on('a', handleA)
        person1.on('a', handleB)
        person1.on('b', handleA)
        person1.on('b', handleB)
        person1.on('b', handleC)

        // 告诉person1,我这个事情不用你管了
        // 1.我告诉你这个事情不用管了，那么就要把这个事件处理后面所有的函数都干掉
        // person1.off('a')//把消息队列里面属于a的数组清空掉
        // person1.off('a', handleA)//告诉你当a发生的时候，你不用做handleA这个事情了，相当于取消订阅了
        person1.off('b', handleB)
        person1.off('b', handleC)

        // person1一旦触发a行为，就要把后面的所有事件处理函数执行掉
        person1.trigger('a')
        console.log(person1)


        function handleA(params) {
            console.log("handleA")
        }
        function handleB(params) {
            console.log("handleB")
        }
        function handleC(params) {
            console.log("handleC")
        }
        function handleD(params) {
            console.log("handleD")
        }
        function handleE(params) {
            console.log("handleD")
        }
        function handleF(params) {
            console.log("handleD")
        }
      
    </script>
</body>

</html>